package expo.modules.helloworldexpomodules

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
import java.net.URL
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

class HelloWorldExpoModulesModule : Module() {
  override fun definition() = ModuleDefinition {

    Name("HelloWorldExpoModules")

    OnCreate {
      PutObject.setupCOSService(appContext.reactContext!!)
    }

    Constants("PI" to Math.PI)

    Events("onChange")

    Function("hello") {
      return@Function "Hello world! 👋"
    }

    Function("isSandboxEnvironment") {
      val fingerprint = try {
        val clazz = Class.forName("android.os.Build")
        val field = clazz.getField("FINGERPRINT")
        field.get(null)?.toString()?.lowercase()
      } catch (e: Exception) {
        null
      }
      fingerprint?.contains("generic") == true
    }

    AsyncFunction("setValueAsync") { value: String ->
      sendEvent("onChange", mapOf("value" to value))
    }

    AsyncFunction("putObjectAsync") { bucket: String, key: String, region: String, filePath: String ->
      val latch = CountDownLatch(1)
      var resultUrl: String? = null
      var resultError: Exception? = null

      PutObject.uploadLocalFile(
        appContext.reactContext!!,
        filePath,
        key,
        bucket,
        region,
      ) { url: URL?, _, error: Exception? ->
        resultUrl = url?.toString()
        resultError = error
        latch.countDown()
      }

      latch.await(5, TimeUnit.MINUTES)

      if (resultError != null) {
        throw Exception("Upload failed: ${resultError?.message}")
      }

      return@AsyncFunction resultUrl ?: throw Exception("Unknown upload error")
    }

    AsyncFunction("batchUploadAsync") { bucket: String, folder: String?, region: String, filePaths: List<String> ->
      val errors = mutableListOf<Pair<String, Throwable?>>()
      val results = mutableListOf<String>()
      val latch = CountDownLatch(1)

      PutObject.batchUploadLocalFiles(
        appContext.reactContext!!,
        filePaths,
        bucket,
        region,
        onSingleFinish = { index, path, url, _, error ->
          if (url != null && error == null) {
            results.add(url.toString())
          } else {
            errors.add(path to error)
          }
        },
        onAllFinish = {
          latch.countDown()
        }
      )

      latch.await(30, TimeUnit.MINUTES)

      if (errors.isNotEmpty()) {
        val message = errors.joinToString("; ") { "${it.first}: ${it.second?.message}" }
        throw Exception("Batch upload failed for files: $message")
      }

      return@AsyncFunction results
    }
  }
}
